import {Orientation, State} from "./enums";
import {randomState, turn} from "./utils";

const Contraposition = new Map<Orientation, Orientation>([
  [Orientation.W, Orientation.S],
  [Orientation.S, Orientation.W],
  [Orientation.A, Orientation.D],
  [Orientation.D, Orientation.A],
])

export class Seal {
  private readonly origin: Object
  private readonly originStep: number

  private W: State = null
  private S: State = null
  private A: State = null
  private D: State = null
  private remainStep: number

  get step(): number {
    return this.remainStep
  }

  constructor() {
    do {
      for (let x in Orientation) {
        if (Reflect.has(this, x)) {
          this[x] = randomState()
        } else {
          throw new Error(`未知方位: ${JSON.stringify(x)}`)
        }
      }
    } while (this.isDone())
    this.origin = {W: this.W, S: this.S, A: this.A, D: this.D}

    this.originStep = Math.random() >= 0.5 ? 5 : 4
    this.remainStep = this.originStep
  }

  public isDone(): Boolean {
    return this._all().every((s) => {
      return s == State.IN
    })
  }

  public isOver(): Boolean {
    return !this.isDone() && this.remainStep <= 0
  }

  public reset() {
    for (let x in Orientation) {
      if (Reflect.has(this, x)) {
        this[x] = this.origin[x]
      }
    }
    this.remainStep = this.originStep
  }

  public input(val: Orientation) {
    if (this.isDone() || this.isOver()) {
      return
    }
    if (!Orientation[val]) {
      throw new Error(`未知输入: ${JSON.stringify(val)}`)
    }
    let con = Contraposition.get(val)
    for (let x in Orientation) {
      if (x != con) {
        if (Reflect.has(this, x)) {
          this[x] = turn(this[x])
        } else {
          throw new Error(`未知方位: ${JSON.stringify(x)}`)
        }
      }
    }
    this.remainStep--
  }

  public view(): Object {
    return {W: this.W, S: this.S, A: this.A, D: this.D}
  }

  public toString(): String {
    return JSON.stringify(this.view())
  }

  private _all(): Array<State> {
    return [this.W, this.S, this.A, this.D]
  }
}
